package msu.ml.data;

import msu.ml.core.*;
import weka.core.*;
import java.io.*;
import java.util.*;

/**
 * The manual validation data provider allows a developer/user to manually
 * specify the number of training/target sets and which files to use in each
 * of those sets.
 *
 * @author Reginald M Mead
 * @version 1.0
 */
public class ManualValidationDataProvider extends DataProvider 
{

   private ArrayList<ValidationSet> validationSets;
   private int currentIndex;

	/**
	 * Creates a new ManualValidationDataProvider.
	 *
	 */
	public ManualValidationDataProvider()
	{
      validationSets = new ArrayList<ValidationSet>();
      this.currentIndex = 0;
	}

	/**
	 * Creates a new ManualValidationDataProvider using the DataCache
	 * specified. 
	 *
	 * @param cache DataCache to use for loading files.
	 */
	public ManualValidationDataProvider(DataCache cache)
	{
      super(cache);
      validationSets = new ArrayList<ValidationSet>();
      this.currentIndex = 0;
	}

	public int count()
	{
		int count = 0;
		for(int i = 0; i < validationSets.size(); i++)
		{
			ValidationSet current = this.validationSets.get(i);
			count += current.targetFiles.length;
		}

		return count;
	}

	/**
	 * add a new validation set to the list of sets that will be returned
	 * when making calls to getNextTrainingData and getNextTargetData.
	 *
	 * @param trainingFiles array of training files
	 * @param targetFiles array of target files
	 */
   public void addValidationSet(String [] trainingFiles, String [] targetFiles)
   {
      this.validationSets.add(new ValidationSet(trainingFiles, targetFiles));
   }


	/**
	 * Provides the next group of target data instances.
	 * Unlike the training data which can be aggregated into
	 * one monolythic instances object, the target data
	 * is provides as an array of instances, each representing
	 * a single sweep. In this way the data can be classified
	 * one sweep at a time.
	 *
	 * @return next group of target data instances
	 */
	public NxInstances [] getNextTargetData()
	{
      ValidationSet current = this.validationSets.get(currentIndex);
      
      NxInstances [] result = new NxInstances[current.targetFiles.length];
      for(int i = 0; i < result.length; i++)
         result[i] = m_DataCache.retrieve(current.targetFiles[i]);

      currentIndex++;
		return result;
	}

   /**
    * Get the names of the data sources used in the
    * next set of training data.
    *
    * @return the labels of the next training data
    */
   public String [] getNextTrainingLabels()
   {
      ValidationSet current = this.validationSets.get(currentIndex);

      String [] result = new String[current.trainingFiles.length];
      for(int i = 0; i < current.trainingFiles.length; i++)
         result[i] = current.trainingFiles[i];

      return result;
   }

	/**
	 * Provides the next set of training instances.
	 *
	 * @return next training data
	 */
   public NxInstances [] getNextIndividualTrainingData()
   {
      ValidationSet current = this.validationSets.get(currentIndex);
      
      NxInstances [] result = new NxInstances[current.trainingFiles.length];
      for(int i = 0; i < result.length; i++)
         result[i] = m_DataCache.retrieve(current.trainingFiles[i]);

		return result;
   }

	/**
	 * Provides the next set of training instances in one
	 * large Instances object.
	 *
	 * @return next training data
	 */
	public NxInstances getNextTrainingData()
	{
      ValidationSet current = this.validationSets.get(currentIndex);

      NxInstances result = null;
      for(int i = 0; i < current.trainingFiles.length; i++)
      {
         if(result == null)
            result = m_DataCache.retrieve(current.trainingFiles[i]);
         else
            result.concatenate(m_DataCache.retrieve(current.trainingFiles[i]));
      }

      return result;
	}

   /**
    * Get the number of validation sets
    *
    * @return the number of validation sets
    */
   public int getNumValidationSets()
   {
      return this.validationSets.size();
   }

	/**
	 * Return whether there is more data 
	 *
	 * @return true if more data exists
	 */
	public boolean hasMoreData()
	{
		return currentIndex < validationSets.size();
	}

   private class ValidationSet
   {
      public String [] trainingFiles;
      public String [] targetFiles;

      public ValidationSet(String [] trainingFiles, String [] targetFiles)
      {
         this.trainingFiles = new String[trainingFiles.length];
         this.targetFiles = new String[targetFiles.length];
         System.arraycopy(trainingFiles, 0, this.trainingFiles, 0, trainingFiles.length);
         System.arraycopy(targetFiles, 0, this.targetFiles, 0, targetFiles.length);
      }
   }
}
